import java.awt.*;
import java.awt.event.InputEvent;
import java.io.File;
import java.net.URL;
import java.util.*;
import java.util.List;

public class Main {
    public static void main(String[] args){
        int[][] matrix = new int[][] {{0,1,0},{0,0,1},{1,1,1}};
        Main main = new Main();
//        int[] ages = new int[] {73,106,39,6,26,15,30,100,71,35,46,112,6,60,110};

//        int n = 5 ;
//        int[][] queries = new int[][] {{2,4},{0,2},{0,4}};
//        main.shortestDistanceAfterQueries(n,queries);

//        int[] colors = new int[] {0,1,0,0,1,0,1};
//        int k = 6;
//        main.numberOfAlternatingGroups(colors,k);

        String[] words = {"abb","aabb","aab","aabc","aabbcc"};
        int ans = main.similarPairs(words);

    }

    public int similarPairs(String[] words) {
        Map<Integer,Integer> map = new HashMap<>();
        int ans = 0 ;
        for(String s: words) {
            int mask = 0;
            for(char c: s.toCharArray()){
                mask |= 1 << (c-'a');
            }
            int c = map.getOrDefault(mask,0);
            ans += c;
            map.put(mask,c+1);
        }
        return ans;
    }

    public int numberOfAlternatingGroups(int[] colors, int k) {
        int n = colors.length;
        int ans = 0;
        int cnt = 0;
        for (int i = 0; i < n * 2; i++) {
            if (i > 0 && colors[i % n] == colors[(i - 1) % n]) {
                cnt = 0;
            }
            cnt++;
            if (i >= n && cnt >= k) {
                ans++;
            }
        }
        return ans;
    }


    //3243. 新增道路查询后的最短距离 I
    public int[] shortestDistanceAfterQueries(int n, int[][] queries) {
        List<Integer>[] from = new ArrayList[n];
        Arrays.setAll(from,i-> new ArrayList<>());
        int[] f = new int[n];
        //每条路的权值
        for(int i=1 ; i<n ; i++){
            f[i] = i;
        }

        int[] ans = new int[queries.length];
        for(int qi=0 ; qi<queries.length ; qi++){
            int l = queries[qi][0];
            int r = queries[qi][1];
            //l就是form[l]的权值
            from[r].add(l);
            if(f[l]+1 < f[r]){
                f[r] = f[l]+1;
                for(int i=r+1 ; i<n ; i++){
                    f[i] = Math.min(f[i] ,f[i-1]+1);
                    for(int j : from[i]){
                        f[i] = Math.min(f[i] ,f[j]+1);
                    }
                }
            }
            ans[qi] = f[n-1];
        }
        return ans;
    }


    public int numSub(String S) {
        final int M = 1000000000+7;
        char[] s = S.toCharArray();
        int n = s.length;
        int left = 0;
        int ans = 0;
        for(int i=0 ; i<n ; i++){
            char c = s[i];
            while(c != '1' && left <= i){
                left++;
            }
            ans += i-left+1;
        }
        return ans;
    }

    public int countKConstraintSubstrings(String S, int k) {
        char[] s = S.toCharArray();
        int n = s.length;
        int left = 0;
        int ans = 0;
        int[] cnt = new int[2];
        for(int i=0; i<n ; i++){
            //s[i]&1 == s[i]%2
            cnt[s[i]&1]++;
            while(cnt[0] > k && cnt[1] > k){
                cnt[s[left] & 1]--;
                left++;
            }
            ans += i-left+1;
        }
        System.out.println(ans);
        return ans ;
    }

    private Set<Integer> set = new HashSet<>();
    public boolean isHappy(int n) {
        if(set.contains(n)){
            return false;
        }
        set.add(n);
        int sum = 0;
        while(n != 0){
            int x = n%10;
            n /= 10;
            sum += x*x;
        }
//        set.add(sum);
        if(sum == 1){
            return true;
        }
        return isHappy(sum) ? true : false;
    }

    public void gameOfLife(int[][] board) {
        int m = board.length;
        int n = board[0].length;
        List<int[]> list = new ArrayList<>();
        for(int i= 0 ; i<m ; i++){
            for(int j=0; j<n ; j++){
                List<Integer> tem = new ArrayList<>();
                int cnt = this.check(board,i,j);
                if(cnt == 3 && board[i][j] == 0){
                    // tem.add(new)
                    list.add(new int[]{1,i,j});
                }else if(cnt > 3 && board[i][j] == 1){
                    list.add(new int[]{0,i,j});
                }else if(cnt < 2 && board[i][j] == 1){
                    list.add(new int[]{0,i,j});
                }
            }
        }
        for(int[] x : list){
            board[x[1]][x[2]] = x[0];
        }
    }
    public int check(int[][] board,int i,int j){
        int m = board.length;
        int n = board[0].length;
        int res = 0 ;
        for(int x =i-1; x<=i+1 ;x++){
            if(x < 0  || x >= m){
                continue;
            }
            for(int y=j-1 ; y<=j+1 ; y++){
                if(y<0 || y>=n || board[x][y] == 0 || (x == i && y == j)){
                    continue;
                }
                res++;
            }
        }
        return res;
    }


    public static int lengthAfterTransformations(String S, int t) {
        char[] s = S.toCharArray();
        List<Character> cut = new ArrayList<>();

        int ans = 0;
        int M = 1_000_000_000+7;
        for(char c: s){
            cut.add(c);
        }
        while(t > 0){
            List<Character> net = new ArrayList<>();
            for(char c : cut){
                if(c == 'z'){
                    net.add('a');
                    net.add('b');
                }else{
                    net.add( (char) (c+1));
                }
            }
            cut = net;
            t--;
        }
        return cut.size()%M;
    }


    //构成整数天的下标对数
    public static long countCompleteDayPairs(int[] hours) {
        long ans = 0L;
        int[] cnt = new int[24];
        for(int h:hours){
            // 先查询 cnt，再更新 cnt，因为题目要求 i < j
            // 如果先更新，再查询，就把 i = j 的情况也考虑进去了
            ans += cnt[(24-h%24)%24] ;
            cnt[h%24]++;
        }
        return ans;
    }

    //robot连点器
    public static  void robot() throws AWTException {
        Robot r = new Robot();
        r.delay(3000);
        int cnt = 0;
        while(true){
            cnt++;
            int i = 0;
            while(i < 3){
                i++;
                r.mouseMove(735,767);
            }
            System.out.println("mouseMove:735,767" );
            while(i < 7){
                i++;
                r.mousePress(InputEvent.BUTTON1_MASK);
                r.mouseRelease(InputEvent.BUTTON1_MASK);
            }
            //停顿160秒
            System.out.println("cnt" + cnt);
            r.delay(60000);
            r.delay(60000);
            r.delay(40000);
        }
    }

    //参数长度n ，操作次数k，数据x，数组；
    //5 3 5
    //4 3 11 2 1
    public static void minMax(){
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int k = sc.nextInt();
        int x = sc.nextInt();
        int[] nums  = new int[n];
        for(int i=0 ; i<n ; i++){
            nums[i] = sc.nextInt();
        }
        int index = 0;
        while(k != 0){
            int max = Integer.MIN_VALUE;
            for(int i=0 ; i<n ; i++){
                if(max != Math.max(max,nums[i])){
                    max = Math.max(max, nums[i]);
                    index = i;
                }
            }
            nums[index] -= x;
            k--;
        }
        Arrays.sort(nums);
        System.out.println(nums[n-1]);

    }

    //给一个s = “21342” k = n；返回最小字符串
    public static String minStr(String s, int k) {
        char[] arr = s.toCharArray();
        String ans = "";
        for(int i = 0 ;i < arr.length-k ; i++){
            if(k > 0 && arr[i] > arr[i+1]){
                k--;
                continue;
            }
            ans += arr[i];
        }
        System.out.println(ans);
        return ans;
    }

    //按位异或^,
    public static int duplicateNumbersXOR(int[] nums) {
        int ans = 0;
        long vis = 0; 
        for (int x : nums) {
            if ((vis >> x & 1) > 0) { // x 在 vis 中
                ans ^= x;
            } else {
                vis |= 1L << x; // 把 x 加到 vis 中
            }
        }
        return ans;
    }


    //完全三叉树查公共夫节点
    public static void three(){
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
            // System.out.println(a + b);
            while( a != b){
                if(a > b){
                    a = (a+1)/3;
                }
                else{
                    b = (b+1)/3;
                }

            }
            System.out.println(a);
        }
    }
}